import React, { useEffect, useState } from 'react';
import * as d3 from 'd3';
import { isEmpty, max, size, sum, trim } from 'lodash';
import { Table } from 'antd';

import { numberFormat } from '@/utils/formatterUtil';

import NumberFormatter from '@/pages/SalaryManage/components/NumberFormatter';
import SensitiveInfo from '@/pages/SalaryManage/components/SensitiveInfo';
import salaryStyle from '@/pages/SalaryManage/salaryManage.less';
import fishHead from './image/fishHead.png';
import fishTail from './image/fishTail.png';
import style from './index.less';

// 当前环境是否为firefox
const isFirefox = /firefox/i.test(navigator.userAgent);

/*  鱼头宽度  */
const FISH_HEAD_WIDTH = 188;
/* 鱼头高度 */
const FISH_HEAD_HEIGHT = 126;
/* 鱼尾宽度 */
const FISH_TAIL_WIDTH = 74;
/* 鱼尾高度 */
const FISH_TAIL_HEIGHT = 126;

/* 第1层级高度 */
const FIRST_LEVEL_HEIGHT = 63;
const FIRST_DOWN_LINE_WIDTH_1 = 30;
const FIRST_DOWN_LINE_WIDTH_2 = 100;
const FIRST_UP_LINE_HEIGHT_1 = FIRST_LEVEL_HEIGHT - 40;
const SECOND_DOWN_LINE_WIDTH_1 = 20;
const SECOND_DOWN_LINE_WIDTH_2 = 100;
/* 收缩、展开的宽度、高度 */
const ICON_RADIUS = 30;

const SECOND_LEVEL_HEIGHT = 40;

/* */
const ITEM_GAP = 20;
/* 垂直方向上的留白高度 */
const VERTICAL_PADDING = 30;
/* 水平方向上的留白宽度 */
const HORIZONTAL_PADDING = 30;
const BODY_PADDING = 20;

const STROKE_WIDTH = 2;
const LINE_COLOR = '#E0E0E0';

/** 是否非数字 */
const notNumber = value => trim(value) === '' || Number.isNaN(Number(value));

const formatTextValue = (hideSensitive, value) => {
    if (notNumber(value)) {
        return value;
    }

    return `${SensitiveInfo({ hide: hideSensitive, children: numberFormat(value, null) })}`
};

const handleHeaderData = (data = [], hideSensitive) =>
    data.map((item, index) =>
        item.children && item.children.length
            ? {
                ...item,
                children: handleHeaderData(item.children, hideSensitive)
            }
            : {
                ...item,
                align: item.align || (item.columnType === 2 ? 'right' : 'left'),
                width: index === item.length - 1 ? '' : item.width || 120,
                children: null,
                render: t =>
                    item.columnType === 2
                        ? <SensitiveInfo hide={hideSensitive}>
                            <NumberFormatter value={t} className={salaryStyle.currency} />
                        </SensitiveInfo>
                        : t
            }
    );

/**
 * 获取一项薪资减项的高度
 * @param usedItem 薪资减项
 * @returns {number}
 */
const getItemHeight = usedItem => {
    const firstLevelHeight = usedItem ? FIRST_LEVEL_HEIGHT : 0;
    const childrenHeight = size(usedItem.children) * SECOND_LEVEL_HEIGHT;

    return firstLevelHeight + childrenHeight;
};

const getDownItemWidth = usedItem => {
    const firstLevelWidth = usedItem ? FIRST_DOWN_LINE_WIDTH_1 + FIRST_DOWN_LINE_WIDTH_2 : 0;
    const childrenWidth =
        size(usedItem.children) > 0
            ? ICON_RADIUS + size(usedItem.children) * SECOND_DOWN_LINE_WIDTH_1 + SECOND_DOWN_LINE_WIDTH_2
            : 0;

    return ITEM_GAP + firstLevelWidth + childrenWidth;
};

const getUpItemWidth = usedItem => {
    const firstLevelWidth = usedItem ? FIRST_DOWN_LINE_WIDTH_1 + FIRST_DOWN_LINE_WIDTH_2 : 0;
    const childrenWidth =
        size(usedItem.children) > 0
            ? ICON_RADIUS + size(usedItem.children) * SECOND_DOWN_LINE_WIDTH_1 + SECOND_DOWN_LINE_WIDTH_2
            : 0;

    return ITEM_GAP + firstLevelWidth + childrenWidth;
};

/** 计算所有下方节点的宽度 */
const calDownItemWidthList = itemList => itemList.map(getDownItemWidth);

/** 计算所有上方节点的宽度 */
const calUpItemWidthList = itemList => itemList.map(getUpItemWidth);

/**
 * 计算容器宽度
 * @param downItemListWidthList 减项所有宽度集合
 * @param upItemListWidthList 加项所有宽度集合
 * @returns {number}
 */
const calContainerWidth = (downItemListWidthList, upItemListWidthList) => {
    const lessItemListWidth = sum(downItemListWidthList) + 3 * ITEM_GAP;
    const addItemListWidth = sum(upItemListWidthList) + ITEM_GAP;
    const result = max([lessItemListWidth, addItemListWidth]);

    return result + HORIZONTAL_PADDING * 2 + BODY_PADDING * 2 + FISH_HEAD_WIDTH + FISH_TAIL_WIDTH;
};

const getBodyY = fishBoneDataList => {
    const usedAddItemList = fishBoneDataList?.usedAddItemList ?? [];
    const addItemListHeight = max(usedAddItemList.map(getItemHeight)) || FISH_HEAD_HEIGHT;

    let result = addItemListHeight;
    if (addItemListHeight < max([FISH_HEAD_HEIGHT, FISH_TAIL_HEIGHT])) {
        result = max([FISH_HEAD_HEIGHT, FISH_TAIL_HEIGHT]);
    }

    return result + VERTICAL_PADDING;
};

/**
 * 计算容器高度
 * @param fishBoneDataList
 * @returns {*}
 */
const calContainerHeight = fishBoneDataList => {
    const usedLessItemList = fishBoneDataList?.usedLessItemList ?? [];
    const lessItemListHeight = max(usedLessItemList.map(getItemHeight));

    const usedAddItemList = fishBoneDataList?.usedAddItemList ?? [];
    const addItemListHeight = max(usedAddItemList.map(getItemHeight)) || FISH_HEAD_HEIGHT;

    let result = lessItemListHeight + addItemListHeight;

    if (result < max([FISH_HEAD_HEIGHT, FISH_TAIL_HEIGHT])) {
        result = max([FISH_HEAD_HEIGHT, FISH_TAIL_HEIGHT]);
    }

    return result + 2 * VERTICAL_PADDING;
};

const calFishBonePosition = dataSource => {
    const bodyY = getBodyY(dataSource);
    const bodyX = HORIZONTAL_PADDING + FISH_HEAD_WIDTH + BODY_PADDING;

    const usedLessItemList = dataSource?.usedLessItemList ?? [];
    const downItemListWidthList = calDownItemWidthList(usedLessItemList);
    let downBeforeX = bodyX + ITEM_GAP;
    const down = usedLessItemList.map((item, i) => {
        const width = downItemListWidthList[i];
        const result = {
            x: downBeforeX,
            width,
            height: getItemHeight(item)
        };
        downBeforeX += width;
        return result;
    });

    const usedAddItemList = dataSource?.usedAddItemList ?? [];
    const upItemListWidthList = calUpItemWidthList(usedAddItemList);
    let upBeforeX = bodyX;
    const up = usedAddItemList.map((item, i) => {
        const width = upItemListWidthList[i];
        const result = {
            x: upBeforeX,
            width,
            height: getItemHeight(item)
        };
        upBeforeX += getUpItemWidth(item);
        return result;
    });

    const containerWidth = calContainerWidth(downItemListWidthList, upItemListWidthList);
    const containerHeight = calContainerHeight(dataSource);

    return {
        containerWidth,
        containerHeight,
        bodyY,
        head: {
            x: HORIZONTAL_PADDING,
            y: bodyY - FISH_HEAD_HEIGHT / 2
        },
        body: {
            x1: HORIZONTAL_PADDING + FISH_HEAD_WIDTH,
            y1: bodyY,
            x2: containerWidth - FISH_TAIL_WIDTH - HORIZONTAL_PADDING,
            y2: bodyY
        },
        tail: {
            x: containerWidth - FISH_TAIL_WIDTH - HORIZONTAL_PADDING,
            y: bodyY - FISH_TAIL_HEIGHT / 2
        },
        up,
        down
    };
};

const clearHideTask = () => {
    const hideTask = document.getElementById('fishToolTip')?.hideTask;

    if (hideTask) {
        hideTask();
    }
};

/**
 * 获取图标的boundingReact,由于火狐在svg中直接获取getBoundingClientRect有问题，所以采用getBBox获取准确的数据
 * @param icon
 * @returns {{top: number, left: number, bottom: number, width: number, right: number, height: number}|*}
 */
const getIconReact = icon => {
    if (isFirefox) {
        const iconBBox = icon.getBBox();
        const fishBox = document.getElementById('fishBoneBox');
        const fishBoxReact = fishBox.getBoundingClientRect();

        return {
            left: fishBoxReact.left + iconBBox.x,
            top: fishBoxReact.top + iconBBox.y,
            width: iconBBox.width,
            height: iconBBox.height,
            right: fishBoxReact.left + iconBBox.x + iconBBox.width,
            bottom: fishBoxReact.left + iconBBox.y + iconBBox.height
        };
    }
    return icon.getBoundingClientRect();
};

const hideTooltip = function hideTooltip(/* d, setDetail */) {
    const task = setTimeout(() => {
        d3.select('#fishToolTipClone').classed('hide', true);
    }, 500);

    document.getElementById('fishToolTip').hideTask = () => {
        window.clearTimeout(task);
        document.getElementById('fishToolTip').hideTask = null;
    };
};

const showTooltip = function showTooltip(d, setDetail) {
    clearHideTask();
    setDetail(d.itemDetailVO);
    if (document.querySelector('body>#fishToolTipClone')) {
        document.body.removeChild(document.querySelector('body>#fishToolTipClone'));
    }
    const fishToolTipClone = document.getElementById('fishToolTip').cloneNode(true);
    fishToolTipClone.id = 'fishToolTipClone';
    document.body.appendChild(fishToolTipClone);
    fishToolTipClone.addEventListener('mouseenter', () => {
        clearHideTask();
    });
    fishToolTipClone.addEventListener('mouseleave', () => {
        hideTooltip()
    });

    d3.select('#fishToolTipClone').classed('hide', false);
    const xPosition = parseFloat(d3.select(this).attr('x'));
    const yPosition = parseFloat(d3.select(this).attr('y'));

    const bodyReact = document.body.getBoundingClientRect();
    const toolTipReact = document.getElementById('fishToolTipClone').getBoundingClientRect();
    const iconReact = getIconReact(this);

    let toolTipX;
    // 探测是否在右边能放得下
    if (iconReact.right + toolTipReact.width < bodyReact.right) {
        // console.log('能放在右边');

        const rightDiff = bodyReact.right - (iconReact.right + toolTipReact.width);
        toolTipX = iconReact.right - bodyReact.left + (rightDiff > 20 ? 20 : rightDiff);
    } else if (iconReact.left - toolTipReact.width > bodyReact.left) {
        // console.log('能放在左边');

        const leftDiff = iconReact.left - toolTipReact.width - bodyReact.left;
        toolTipX = iconReact.left - bodyReact.left - toolTipReact.width - (leftDiff > 20 ? 20 : leftDiff);
    } else {
        // console.log('两边都放不下')
        toolTipX = xPosition - toolTipReact.width / 2;
    }

    let toolTipY;
    if (iconReact.top - toolTipReact.height > bodyReact.top) {
        // console.log('能放在上面');
        toolTipY = iconReact.top - toolTipReact.height - bodyReact.top + 20;
    } else if (iconReact.top + toolTipReact.height < bodyReact.bottom) {
        // console.log('能放在下面');
        toolTipY = iconReact.top - bodyReact.top - 20;
    } else {
        toolTipY = yPosition - toolTipReact.height / 2;
        // console.log('上下都不下');
    }

    d3.select('#fishToolTipClone')
        .style('left', `${toolTipX}px`)
        .style('top', `${toolTipY}px`);
};

/**
 * 创建鱼头
 */
const createFishBoneHead = ({ x, y, headVo, hideSensitive }) => {
    const fishHeadGroup = d3.create('svg:g').attr('class', 'fish-head');

    fishHeadGroup
        .append('svg:image')
        .attr('x', x)
        .attr('y', y)
        .attr('width', FISH_HEAD_WIDTH)
        .attr('height', FISH_HEAD_HEIGHT)
        .attr('xlink:href', fishHead);

    fishHeadGroup
        .append('svg:text')
        .attr('x', x + FISH_HEAD_WIDTH)
        .attr('y', y + FISH_HEAD_HEIGHT / 2)
        .attr('dx', '-12px')
        .attr('dy', '-6px')
        .attr('text-anchor', 'end')
        .attr('class', 'fish_text-label')
        .text('实发薪资');

    fishHeadGroup
        .append('svg:text')
        .attr('x', x + FISH_HEAD_WIDTH)
        .attr('y', y + FISH_HEAD_HEIGHT / 2)
        .attr('dx', '-12px')
        .attr('dy', '1.2em')
        .attr('text-anchor', 'end')
        .attr('class', `fish_text-value ${notNumber(headVo) ? 'fish_text-value--des' : ''}`)
        .text(formatTextValue(hideSensitive, headVo))

    return fishHeadGroup;
};

/**
 * 创建鱼主骨
 */
const createFishBody = ({ x1, y1, x2, y2 }) => {
    const fishBody = d3.create('svg:line');

    fishBody
        .attr('x1', x1)
        .attr('y1', y1)
        .attr('x2', x2)
        .attr('y2', y2)
        .style('stroke', LINE_COLOR)
        .style('stroke-width', STROKE_WIDTH);

    return fishBody;
};

/** 创建薪资展开的按钮 */
const createExpandIcon = ({ startX, startY }) => {
    // 添加展开的按钮
    const expandIcon = d3.create('svg:use');

    expandIcon
        .attr('xlink:href', '#iconExpand')
        .attr('x', `${startX - ICON_RADIUS / 2}`)
        .attr('y', `${startY - ICON_RADIUS / 2}`);

    return expandIcon;
};

/** 创建薪资收缩按钮 */
const createCollapseIcon = ({ startX, startY, count }) => {
    const cloneCollapseIcon = d3
        .select('#iconCollapsedTemplate')
        .clone(true)
        .attr('id', null)
        .attr('x', `${startX - ICON_RADIUS / 2}`)
        .attr('y', `${startY - ICON_RADIUS / 2}`);

    cloneCollapseIcon.select('.icon-collapsed-text').text(count);

    return cloneCollapseIcon;
};

/** 创建薪资减项一级节点 */
const createFirstDownNode = ({ bodyY, firsItemStartX, radius = 10, item, hideSensitive }) => {
    const firstLevelGroup = d3.create('svg:g');
    firstLevelGroup.attr('class', 'fish-group fish-group--down level-1');

    const firstLevelNode = firstLevelGroup.append('g').attr('class', 'fish-node');
    firstLevelNode
        .append('path')
        .attr(
            'd',
            `M ${firsItemStartX}, ${bodyY}
             L ${firsItemStartX + FIRST_DOWN_LINE_WIDTH_1 - radius} ${bodyY + FIRST_LEVEL_HEIGHT - radius}
             Q ${firsItemStartX + FIRST_DOWN_LINE_WIDTH_1 - radius / 2} ${bodyY + FIRST_LEVEL_HEIGHT}
               ${firsItemStartX + FIRST_DOWN_LINE_WIDTH_1} ${bodyY + FIRST_LEVEL_HEIGHT}
             L ${firsItemStartX + FIRST_DOWN_LINE_WIDTH_1 + FIRST_DOWN_LINE_WIDTH_2} ${bodyY + FIRST_LEVEL_HEIGHT}
            `
        )
        .attr('fill', 'transparent')
        .style('stroke', LINE_COLOR)
        .style('stroke-width', STROKE_WIDTH);

    const textG = firstLevelNode.append('g').attr('class', 'fish-text');

    textG
        .append('text')
        .attr('x', `${firsItemStartX + FIRST_DOWN_LINE_WIDTH_1 + radius}`)
        .attr('y', `${bodyY + FIRST_LEVEL_HEIGHT - 20}`)
        .attr('dy', -6)
        .attr('class', 'fish_text-label')
        .text(item.title);

    textG
        .append('text')
        .attr('x', `${firsItemStartX + FIRST_DOWN_LINE_WIDTH_1 + radius}`)
        .attr('y', `${bodyY + FIRST_LEVEL_HEIGHT - 5}`)
        .attr('fill', 'red')
        .attr('class', `fish_text-value ${notNumber(item.value) ? 'fish_text-value--des' : ''}`)
        .text(formatTextValue(hideSensitive, item.value))

    return firstLevelGroup;
};

/** 创建薪资减项二级节点 */
const createSecondDownNode = ({ children, startX, startY }, setDetail, hideSensitive) => {
    // 添加二级项
    const secondGroup = d3.create('svg:g').attr('class', 'fish-group level-2');
    const childrenEle = secondGroup
        .selectAll('g')
        .data(children)
        .enter();
    const childrenG = childrenEle.append('g').attr('class', 'fish-node');

    const HALF_ICON_RADIUS = ICON_RADIUS / 2;
    childrenG
        .append('path')
        .attr(
            'd',
            (d, i) => `
                M ${startX} ${startY}
                Q ${startX + (ICON_RADIUS * 3) / 4} ${startY}
                  ${startX + ICON_RADIUS} ${startY + HALF_ICON_RADIUS}
                L ${startX + ICON_RADIUS + (i + 1) * SECOND_DOWN_LINE_WIDTH_1 - HALF_ICON_RADIUS}
                  ${startY + (i + 1) * SECOND_LEVEL_HEIGHT - HALF_ICON_RADIUS}
                Q ${startX + ICON_RADIUS + (i + 1) * SECOND_DOWN_LINE_WIDTH_1 - HALF_ICON_RADIUS / 2}
                  ${startY + (i + 1) * SECOND_LEVEL_HEIGHT}
                  ${startX + ICON_RADIUS + (i + 1) * SECOND_DOWN_LINE_WIDTH_1}
                  ${startY + (i + 1) * SECOND_LEVEL_HEIGHT}
                H ${startX + ICON_RADIUS + (i + 1) * SECOND_DOWN_LINE_WIDTH_1 + SECOND_DOWN_LINE_WIDTH_2}
            `
        )
        .attr('fill', 'transparent')
        .style('stroke', LINE_COLOR)
        .style('stroke-width', STROKE_WIDTH);

    const childrenGText = childrenG.append('text').attr('class', 'fish-text');
    childrenGText.attr('x', (d, i) => `${startX + ICON_RADIUS + (i + 1) * SECOND_DOWN_LINE_WIDTH_1}`);
    childrenGText.attr('y', (d, i) => `${startY + (i + 1) * SECOND_LEVEL_HEIGHT - 5}`);
    childrenGText
        .append('tspan')
        .attr('class', 'fish_text-label')
        .text(d => `${d.title}`);
    childrenGText
        .append('tspan')
        .attr('class', d => `fish_text-value ${notNumber(d.value) ? 'fish_text-value--des' : ''}`)
        .text(d => ` ${formatTextValue(hideSensitive, d.value)}`);

    // 添加详情icon
    childrenG
        .filter(d => !isEmpty(d?.itemDetailVO))
        .append('svg:use')
        .classed('icon-detail-use', true)
        .attr('xlink:href', '#detailIcon')
        .attr(
            'x',
            (d, i) => `${startX + ICON_RADIUS + (i + 1) * SECOND_DOWN_LINE_WIDTH_1 + SECOND_DOWN_LINE_WIDTH_2 + 10}`
        )
        .attr('y', (d, i) => `${startY + (i + 1) * SECOND_LEVEL_HEIGHT - 16}`)
        .on('mouseenter', function mouseenter(d) {
            d3.select(this).classed('icon-detail-use-enter', true);
            showTooltip.apply(this, [d, setDetail]);
        })
        .on('mouseleave', function mouseout(d) {
            d3.select(this).classed('icon-detail-use-enter', false);
            hideTooltip.apply(this, [d, setDetail]);
        });

    return secondGroup;
};

const createDownGroup = ({ fishPosition, downItem, i }, setDetail, hideSensitive) => {
    const { bodyY } = fishPosition;
    // 添加一级项
    const firsItemStartX = fishPosition.down[i].x + ITEM_GAP;
    const firstLevelGroup = createFirstDownNode({
        bodyY,
        firsItemStartX,
        item: downItem,
        hideSensitive
    });

    const { children } = downItem;

    if (size(children) === 0) {
        return firstLevelGroup;
    }

    const startX = firsItemStartX + FIRST_DOWN_LINE_WIDTH_1 + FIRST_DOWN_LINE_WIDTH_2;
    const startY = bodyY + FIRST_LEVEL_HEIGHT;

    // 添加二级项
    const secondGroupNode = createSecondDownNode(
        {
            children,
            startX,
            startY
        },
        setDetail,
        hideSensitive
    ).node();
    const secondGroup = firstLevelGroup.append(() => secondGroupNode);
    // 添加展开的按钮
    const expandIcon = firstLevelGroup.append(() =>
        createExpandIcon({
            startX: startX + ICON_RADIUS / 2,
            startY
        }).node()
    );
    // 添加收缩按钮
    const collapseIcon = firstLevelGroup.append(() =>
        createCollapseIcon({
            startX: startX + ICON_RADIUS / 2,
            startY,
            count: children.length
        }).node()
    );

    expandIcon
        .on('click', () => {
            collapseIcon.classed('hide', false);
            expandIcon.classed('hide', true);
            secondGroup.selectAll('.fish-node').style('opacity', 0).style('display', 'none');
        })
        .classed('hide', false);

    collapseIcon
        .on('click', () => {
            collapseIcon.classed('hide', true);
            expandIcon.classed('hide', false);
            secondGroup.selectAll('.fish-node').style('opacity', 1).style('display', 'block');
        })
        .classed('hide', true);

    return firstLevelGroup;
};

/** 创建薪资加项一级节点 */
const createFirstUpNode = ({ bodyY, firsItemStartX, radius = 10, item, hideSensitive }) => {
    const firstLevelGroup = d3.create('svg:g');
    firstLevelGroup.attr('class', 'fish-group fish-group--up level-1');

    const firstLevelNode = firstLevelGroup.append('g').attr('class', 'fish-node');

    firstLevelNode
        .append('path')
        .attr(
            'd',
            `M ${firsItemStartX}, ${bodyY}
             L ${firsItemStartX + FIRST_DOWN_LINE_WIDTH_1 - radius} ${bodyY - FIRST_UP_LINE_HEIGHT_1 + radius / 2}
             Q ${firsItemStartX + FIRST_DOWN_LINE_WIDTH_1} ${bodyY - FIRST_UP_LINE_HEIGHT_1}
               ${firsItemStartX + FIRST_DOWN_LINE_WIDTH_1 + radius / 2} ${bodyY - FIRST_UP_LINE_HEIGHT_1}
             L ${firsItemStartX + FIRST_DOWN_LINE_WIDTH_1 + FIRST_DOWN_LINE_WIDTH_2} ${bodyY - FIRST_UP_LINE_HEIGHT_1}
            `
        )
        .attr('fill', 'transparent')
        .style('stroke', LINE_COLOR)
        .style('stroke-width', STROKE_WIDTH);

    const textG = firstLevelNode.append('g').attr('class', 'fish-text');

    textG
        .append('text')
        .attr('x', `${firsItemStartX + FIRST_DOWN_LINE_WIDTH_1 + radius}`)
        .attr('y', `${bodyY - FIRST_LEVEL_HEIGHT + 20}`)
        .attr('dy', -6)
        .attr('class', 'fish_text-label')
        .text(item.title);

    textG
        .append('text')
        .attr('x', `${firsItemStartX + FIRST_DOWN_LINE_WIDTH_1 + radius}`)
        .attr('y', `${bodyY - FIRST_LEVEL_HEIGHT + 35}`)
        .attr('fill', 'red')
        .attr('class', `fish_text-value ${notNumber(item.value) ? 'fish_text-value--des' : ''}`)
        .text(` ${formatTextValue(hideSensitive, item.value)}`);

    return firstLevelGroup;
};

const createSecondUpNode = ({ children, startX, startY }, setDetail, hideSensitive) => {
    // 添加二级项
    const secondGroup = d3.create('svg:g').attr('class', 'fish-group level-2');
    const childrenEle = secondGroup
        .selectAll('g')
        .data(children)
        .enter();
    const childrenG = childrenEle.append('g').attr('class', 'fish-node');

    const HALF_ICON_RADIUS = ICON_RADIUS / 2;
    childrenG
        .append('path')
        .attr(
            'd',
            (d, i) => `
                M ${startX} ${startY}
                Q ${startX + (ICON_RADIUS * 3) / 4} ${startY}
                  ${startX + ICON_RADIUS} ${startY - HALF_ICON_RADIUS}
                L ${startX + ICON_RADIUS + (i + 1) * SECOND_DOWN_LINE_WIDTH_1 - HALF_ICON_RADIUS}
                  ${startY - (i + 1) * SECOND_LEVEL_HEIGHT + HALF_ICON_RADIUS}
                Q ${startX + ICON_RADIUS + (i + 1) * SECOND_DOWN_LINE_WIDTH_1 - HALF_ICON_RADIUS / 2}
                  ${startY - (i + 1) * SECOND_LEVEL_HEIGHT}
                  ${startX + ICON_RADIUS + (i + 1) * SECOND_DOWN_LINE_WIDTH_1}
                  ${startY - (i + 1) * SECOND_LEVEL_HEIGHT}
                H ${startX + ICON_RADIUS + (i + 1) * SECOND_DOWN_LINE_WIDTH_1 + SECOND_DOWN_LINE_WIDTH_2}
            `
        )
        .attr('fill', 'transparent')
        .style('stroke', LINE_COLOR)
        .style('stroke-width', STROKE_WIDTH);

    const childrenGText = childrenG.append('text').attr('class', 'fish-text');
    childrenGText.attr('x', (d, i) => `${startX + ICON_RADIUS + (i + 1) * SECOND_DOWN_LINE_WIDTH_1}`).attr('dx', 6);
    childrenGText.attr('y', (d, i) => `${startY - (i + 1) * SECOND_LEVEL_HEIGHT - 5}`);
    childrenGText
        .append('tspan')
        .attr('class', 'fish_text-label')
        .text(d => `${d.title}`);
    childrenGText
        .append('tspan')
        .attr('class', d => `fish_text-value ${notNumber(d.value) ? 'fish_text-value--des' : ''}`)
        .text(d => ` ${formatTextValue(hideSensitive, d.value)}`);

    // 添加详情icon
    childrenG
        .filter(d => !isEmpty(d?.itemDetailVO))
        .append('svg:use')
        .classed('icon-detail-use', true)
        .attr('xlink:href', '#detailIcon')
        .attr(
            'x',
            (d, i) => `${startX + ICON_RADIUS + (i + 1) * SECOND_DOWN_LINE_WIDTH_1 + SECOND_DOWN_LINE_WIDTH_2 + 10}`
        )
        .attr('y', (d, i) => `${startY - (i + 1) * SECOND_LEVEL_HEIGHT - 16}`)
        .on('mouseenter', function mouseenter(d) {
            d3.select(this).classed('icon-detail-use-enter', true);
            showTooltip.apply(this, [d, setDetail]);
        })
        .on('mouseleave', function mouseout(d) {
            d3.select(this).classed('icon-detail-use-enter', false);
            hideTooltip.apply(this, [d, setDetail]);
        });

    return secondGroup;
};

const createUpGroup = ({ fishPosition, upItem, i }, setDetail, hideSensitive) => {
    const { bodyY } = fishPosition;
    // 添加一级项
    const radius = 10;
    const firsItemStartX = fishPosition.up[i].x + ITEM_GAP;
    const firstLevelGroup = createFirstUpNode({
        bodyY,
        firsItemStartX,
        radius,
        item: upItem,
        hideSensitive
    });

    const { children = [] } = upItem;

    if (size(children) === 0) {
        return firstLevelGroup;
    }

    const startX = firsItemStartX + FIRST_DOWN_LINE_WIDTH_1 + FIRST_DOWN_LINE_WIDTH_2;
    const startY = bodyY - FIRST_UP_LINE_HEIGHT_1;

    // 添加二级项
    const secondGroupNode = createSecondUpNode(
        {
            children,
            startX,
            startY
        },
        setDetail,
        hideSensitive
    ).node();
    const secondGroup = firstLevelGroup.append(() => secondGroupNode);

    // 添加展开的按钮
    const expandIcon = firstLevelGroup.append(() =>
        createExpandIcon({
            startX: startX + ICON_RADIUS / 2,
            startY
        }).node()
    );
    // 添加收缩按钮
    const collapseIcon = firstLevelGroup.append(() =>
        createCollapseIcon({
            startX: startX + ICON_RADIUS / 2,
            startY,
            count: children.length
        }).node()
    );

    expandIcon
        .on('click', () => {
            collapseIcon.classed('hide', false);
            expandIcon.classed('hide', true);
            secondGroup.selectAll('.fish-node').style('opacity', 0).style('display', 'none');
        })
        .classed('hide', false);

    collapseIcon
        .on('click', () => {
            collapseIcon.classed('hide', true);
            expandIcon.classed('hide', false);
            secondGroup.selectAll('.fish-node').style('opacity', 1).style('display', 'block');
        })
        .classed('hide', true);

    return firstLevelGroup;
};

/**
 * 创建鱼尾
 */
const createFishBoneTail = () => {
    const fishBoneHead = d3.create('svg:image');

    // 添加鱼尾
    fishBoneHead
        .attr('width', FISH_TAIL_WIDTH)
        .attr('height', FISH_TAIL_HEIGHT)
        .attr('xlink:href', fishTail);

    return fishBoneHead;
};

const FishBone = ({ dataSource, hideSensitive = true }) => {
    const [detail, setDetail] = useState({});
    useEffect(() => {
        if (!dataSource || dataSource.length === 0) {
            return;
        }

        const fishPosition = calFishBonePosition(dataSource);
        console.log('fishPosition:', fishPosition);

        const root = d3
            .select('#fishBoneSvg')
            .attr('viewBox', `0 0 ${fishPosition.containerWidth} ${fishPosition.containerHeight}`)
            .attr('width', fishPosition.containerWidth)
            .attr('height', fishPosition.containerHeight);

        const defs = root.select('defs').clone(true);
        root.html('').append(() => defs.node());

        // 添加鱼头
        root.append(() =>
            createFishBoneHead({
                x: fishPosition.head.x,
                y: fishPosition.head.y,
                headVo: dataSource.actualSalary,
                hideSensitive
            }).node()
        );

        const upTitle = d3
            .select('#upTitle')
            .clone(true)
            .attr('id', null)
            .attr('x', `${HORIZONTAL_PADDING + FISH_HEAD_WIDTH + 16}`)
            .attr('y', `${fishPosition.bodyY - FIRST_LEVEL_HEIGHT - 63}`);

        upTitle.select('.fish_title-text').text('加项');
        root.append(() => upTitle.node());

        const downTitle = d3
            .select('#downTitle')
            .clone(true)
            .attr('id', null)
            .attr('x', `${HORIZONTAL_PADDING + FISH_HEAD_WIDTH + 16}`)
            .attr('y', `${fishPosition.bodyY + FIRST_LEVEL_HEIGHT + 26}`);

        downTitle.select('.fish_title-text').text('减项');
        root.append(() => downTitle.node());

        // 添加鱼主骨
        root.append(() => createFishBody(fishPosition.body).node());

        // 添加减项
        const downGroup = root
            .selectAll('.fish-group--down')
            .data(dataSource.usedLessItemList || [])
            .enter()
            .append((downItem, i) =>
                createDownGroup(
                    {
                        fishPosition,
                        downItem,
                        i
                    },
                    setDetail,
                    hideSensitive
                ).node()
            );

        // 当文字过长时，重新调整文字排序
        downGroup.selectAll('.level-2 .fish-text').each((d, index, nodes) => {
            const node = nodes[index];
            const textLength = node.getComputedTextLength();
            const $node = d3.select(node);

            if (textLength > SECOND_DOWN_LINE_WIDTH_2) {
                const $label = $node.select('.fish_text-label');
                const $value = $node.select('.fish_text-value');
                $label.attr('x', +$node.attr('x')).attr('y', +$node.attr('y') - 15);
                $value.attr('x', +$node.attr('x')).attr('y', $node.attr('y'));
            }
        });

        // 添加进项
        const upGroup = root
            .selectAll('.fish-group--up')
            .data(dataSource.usedAddItemList || [])
            .enter()
            .append((upItem, i) =>
                createUpGroup(
                    {
                        fishPosition,
                        upItem,
                        i
                    },
                    setDetail,
                    hideSensitive
                ).node()
            );

        // 当文字过长时，重新调整文字排序
        upGroup.selectAll('.level-2 .fish-text').each((d, index, nodes) => {
            const node = nodes[index];
            const textLength = node.getComputedTextLength();
            const $node = d3.select(node);

            if (textLength > SECOND_DOWN_LINE_WIDTH_2) {
                const $label = $node.select('.fish_text-label');
                const $value = $node.select('.fish_text-value');
                $label.attr('x', +$node.attr('x') + 12).attr('y', +$node.attr('y') - 15);
                $value.attr('x', +$node.attr('x') + 12).attr('y', $node.attr('y'));
            }
        });

        // 当一级项金额长度过长时
        root.selectAll('.level-1>.fish-node .fish-text').each((d, index, nodes) => {
            const node = nodes[index];
            const $node = d3.select(node);
            const textLength = $node
                .select('.fish_text-value')
                .node()
                .getComputedTextLength();

            if (textLength > FIRST_DOWN_LINE_WIDTH_2 - 10) {
                $node
                    .select('.fish_text-value')
                    .attr('textLength', FIRST_DOWN_LINE_WIDTH_2 - 10)
                    .attr('lengthAdjust', 'spacingAndGlyphs');
            }
        });

        // 添加鱼尾
        root.append(() => createFishBoneTail().node())
            .attr('x', fishPosition.tail.x)
            .attr('y', fishPosition.tail.y);
    }, [dataSource, hideSensitive]);

    return (
        <div className={style.wrapper}>
            <div id="fishBoneBox" className="box">
                <svg id="fishBoneSvg">
                    <defs>
                        {/* 向上箭头 */}
                        <svg
                            id="upTitle"
                            viewBox="0 0 82 36"
                            className="fish_title fish_title--up"
                            width="82"
                            height="36"
                        >
                            <rect x="0" y="0" width="82" height="36" rx="8" ry="8" />
                            <svg x="9" y="9" viewBox="0 0 1024 1024" width="17" height="17">
                                <path
                                    d="M512 64.383234C264.7878 64.383234 64.383234 264.7878 64.383234 512s200.404567 447.616766 447.616766 447.616766 447.616766-200.404567 447.616766-447.616766S759.2122 64.383234 512 64.383234zM649.153661 476.550387c-14.514842 12.958403-36.414339 11.264-48.914906-3.784303l-50.402874-60.683752 0 287.768527c0 19.858651-15.528623 35.958547-34.684168 35.958547s-34.684168-16.098874-34.684168-35.958547L480.467545 409.174866l-57.516711 64.531417c-13.000303 14.585357-34.942723 15.483657-49.011992 2.005078-14.068248-13.477557-14.933844-36.227321-1.934563-50.812679l106.756599-119.777341c20.609788-23.122778 55.10285-22.477924 74.964567 1.433804l99.077621 119.283737C665.302611 440.886164 663.668503 463.590962 649.153661 476.550387z"
                                    fill="#44C69D"
                                />
                            </svg>
                            <text className="fish_title-text" x="50%" y="50%" textAnchor="middle" dx=".3em" dy=".3em" />
                        </svg>

                        {/* 向下箭头 */}
                        <svg
                            id="downTitle"
                            viewBox="0 0 82 36"
                            className="fish_title fish_title--down"
                            width="82"
                            height="36"
                        >
                            <rect x="0" y="0" width="82" height="36" rx="8" ry="8" />
                            <svg x="9" y="9" viewBox="0 0 1024 1024" width="17" height="17">
                                <path
                                    d="M512 959.616766C759.2122 959.616766 959.616766 759.2122 959.616766 512s-200.40456701-447.616766-447.616766-447.616766-447.616766 200.40456701-447.616766 447.616766S264.7878 959.616766 512 959.616766zM374.846339 547.449613c14.514842-12.958403 36.414339-11.264 48.914906 3.784303l50.402874 60.683752 0-287.768527c0-19.858651 15.528623-35.958547 34.684168-35.958547s34.684168 16.098874 34.684168 35.958547L543.532455 614.825134l57.516711-64.531417c13.000303-14.585357 34.942723-15.483657 49.011992-2.005078 14.068248 13.477557 14.933844 36.227321 1.934563 50.812679l-106.756599 119.777341c-20.609788 23.122778-55.10285 22.477924-74.964567-1.433804l-99.077621-119.283737C358.697389 583.113836 360.33149701 560.409038 374.846339 547.449613z"
                                    fill="#FF5959"
                                />
                            </svg>
                            <text className="fish_title-text" x="50%" y="50%" textAnchor="middle" dx=".3em" dy=".3em" />
                        </svg>

                        <g id="iconExpand" className="icon-expand" width="30" height="30" style={{ cursor: 'pointer' }}>
                            <svg viewBox="0 0 1024 1024" width="30" height="30">
                                <circle
                                    className="icon-expand-circle"
                                    cx="512"
                                    cy="512"
                                    r="450"
                                    stroke="#E0E0E0"
                                    strokeWidth="62"
                                    fill="white"
                                />
                                <path
                                    className="icon-expand-minus"
                                    d="M752.939742 542.124131h-481.875267a30.117204 30.117204 0 0 1 0-60.234408h481.875267a30.117204 30.117204 0 0 1 0 60.234408z"
                                    stroke="black"
                                    fill="black"
                                />
                            </svg>
                        </g>
                        <svg
                            id="iconCollapsedTemplate"
                            viewBox="0 0 1024 1024"
                            width="30"
                            height="30"
                            className="icon-collapsed"
                        >
                            <circle className="icon-collapsed-circle" cx="512" cy="512" r="450" fill="#aeaeae" />
                            <text className="icon-collapsed-text" x="50%" y="50%" textAnchor="middle" dy=".3em" />
                        </svg>

                        {/* 详情按钮 */}
                        <g
                            id="detailIcon"
                            className="icon-detail"
                            width={21}
                            height={21}
                        >
                            <svg
                                viewBox="0 0 1024 1024"
                                version="1.1"
                                xmlns="http://www.w3.org/2000/svg"
                                width="21"
                                height="21"
                            >
                                <path d="M288.042667 170.666667l282.56 0.085333c1.408 0 2.730667 0.213333 4.096 0.426667a31.146667 31.146667 0 0 1 9.237333 2.602666 30.506667 30.506667 0 0 1 7.637333 4.992l1.706667 1.28 16.085333 16.085334 191.765334 189.866666a32 32 0 0 1 9.472 22.762667l0.042666 369.898667c0 19.946667-7.765333 38.698667-21.866666 52.8A74.176 74.176 0 0 1 736 853.333333h-448c-41.173333 0-74.666667-33.493333-74.666667-74.666666v-533.333334c0-19.925333 7.765333-38.677333 21.866667-52.778666A74.112 74.112 0 0 1 287.957333 170.666667h0.085334z m-0.064 64l-2.133334 0.213333a10.645333 10.645333 0 0 0-8.533333 10.453333v533.333334a10.666667 10.666667 0 0 0 10.666667 10.666666h448a10.602667 10.602667 0 0 0 10.666666-10.666666l-0.064-336H592a53.397333 53.397333 0 0 1-53.333333-53.333334v-154.581333L288 234.666667zM640 618.88a32 32 0 1 1 0 64H384a32 32 0 1 1 0-64z m-149.333333-128a32 32 0 1 1 0 64h-106.666667a32 32 0 1 1 0-64z m112-210.944v98.730667h98.752l-98.773334-98.730667z" />
                                <rect x="0" y="0" width="1024" height="1024" fillOpacity="0" />
                            </svg>
                        </g>
                    </defs>
                </svg>
            </div>
            <div id="fishToolTip" className="fishbone-tip hide" onMouseEnter={clearHideTask} onMouseLeave={hideTooltip}>
                <div className="fishbone-tip_head">计算公式</div>
                <div className="fishbone-tip_body">
                    <div className="title">
                        {detail?.title}
                        {detail?.tips ? ' = ' : null}
                        {detail?.tips}
                    </div>
                    {detail?.remark ? <div className="remark">注：{detail.remark}</div> : null}
                    {detail?.conditionItems ? <div className="title" style={{ marginTop: 16 }}>详细参数：</div> : null}
                    <div className="fishbone-tip_body_table">
                        <Table
                            columns={handleHeaderData(detail?.conditionItems, hideSensitive)}
                            dataSource={detail?.data}
                            size="small"
                            pagination={false}
                            scroll={{
                                x: 'max-content'
                            }}
                        />
                    </div>
                </div>
            </div>
        </div>
    );
};

export default FishBone;
